﻿# coding: utf-8

import os

import wx
import wx.lib.colourselect as csel
import wx.lib.scrolledpanel as scrolled
import xml.dom.minidom as minixml

import Entity
import EntityModel
import Model
import SceneNode
import Resource
import Mesh
import Material
import TextureMapping
import MPAlphaBlend
import MPAlphaTest
import MPCullingMode
import MPDepthState
import MPAmbient
import MPDiffuse
import MPSpecular

from wxFramework.Dialog import wxDialogMaterialEdit

class wxPanelPropertyMaterial(scrolled.ScrolledPanel):
    def __init__(self, parent, id):
        scrolled.ScrolledPanel.__init__(self, parent, id)
        
        # material data.
        self._sceneNodeRef = None
        self._entityHandle = 0
        
        # layout all UI.
        sizerSubMesh = self.__CreateUISubMeshList()
        sizerMaterial = self.__CreateUIMaterialList()
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(sizerSubMesh, 1, wx.EXPAND|wx.ALL, border = 5)
        sizer.Add(sizerMaterial, 1, wx.EXPAND|wx.ALL, border = 5)
        self.SetSizer(sizer)
        
        # bind all event.
        self._listSubMesh.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.__OnSubMeshMtlIdxChanged)
        self._listMaterial.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.__OnActiveMaterial)
        self._listMaterial.Bind(wx.EVT_RIGHT_DOWN, self.__OnRightClickMaterialList)
        
        # define popup menu.
        self.__DefinePopupMenu()
        
    def IsEditing(self):
        return self._sceneNodeRef and self._sceneNodeRef() and self._entityHandle
        
    def __DefinePopupMenu(self):
        self._idSaveMaterial = wx.NewId()
        self._idSaveMaterialAs = wx.NewId()
        self._idSaveModelAs = wx.NewId()
        
        self.Bind(wx.EVT_MENU, self.__OnMenuSaveMaterial, id = self._idSaveMaterial)
        self.Bind(wx.EVT_MENU, self.__OnMenuSaveMaterialAs, id = self._idSaveMaterialAs)
        self.Bind(wx.EVT_MENU, self.__OnMenuSaveModelAs, id = self._idSaveModelAs)
        
    def __OnRightClickMaterialList(self, e):
        menu = wx.Menu()
        menu.Append(self._idSaveMaterial, TD.SAVE_MATERIAL)
        menu.Append(self._idSaveMaterialAs, TD.SAVE_MATERIAL_AS)
        menu.Append(self._idSaveModelAs, TD.SAVE_MODEL_AS)
        self._listMaterial.PopupMenu(menu)
        menu.Destroy()
        
    def __OnMenuSaveMaterial(self, e):
        if not self.IsEditing():
            return
            
        idx = self._listMaterial.GetFirstSelected()
        if idx == -1:
            return
            
        ret, materialHandle, groupSubMeshCount = EntityModel.GetMaterialInfo(self._entityHandle, idx)
        if not ret:
            return

        mtlFileName = Material.GetFileName(materialHandle)
        fullPath = os.path.join(GLOBAL_SETTING._workDir, mtlFileName)
        self.__SaveMaterialFile(fullPath, materialHandle)
        self.__RefreshAllModel(mtlFileName)
        
    def __RefreshAllModel(self, mtlFileName):
        def __CallbackRefreshModel(node, param):
            entityCount = SceneNode.GetEntityCount(node.GetCoreHandle())
            for i in xrange(0, entityCount):
                entityHandle = SceneNode.GetEntity(node.GetCoreHandle(), i)
                entityType = Entity.GetType(entityHandle)
                if entityType != Entity.ET_MODEL:
                    continue
                    
                mtlCount = EntityModel.GetMaterialCount(entityHandle)
                for j in xrange(0, mtlCount):
                    ret, mtlHandle, groupSubMeshCount = EntityModel.GetMaterialInfo(entityHandle, j)
                    if ret and (Material.GetFileName(mtlHandle) == param):
                        ret = Material.LoadFromFile(mtlHandle, param)
                        if not ret:
                            dlg = GMD.GenericMessageDialog(self, TD.ERR_LOAD_FILE_FAILED, TD.ERROR, wx.OK | wx.ICON_ERROR)
                            dlg.ShowModal()
                            dlg.Destroy()
        
        param = mtlFileName
        SCENE_EDITOR.TraceAllSceneNode(__CallbackRefreshModel, param)
        
    def __SaveMaterialFile(self, fileName, materialHandle):
        """ Save material to file """
        
        # write material file.
        impl = minixml.getDOMImplementation()
        newdoc = impl.createDocument(None, None, None)

        mtlElem = newdoc.createElement('Material')
        newdoc.childNodes.append(mtlElem)
        
        # texture mapping.
        mapCount = TextureMapping.GetMapCount(materialHandle)
        for i in xrange(0, mapCount):
            ret, texFileName = TextureMapping.GetMapTextureFile(materialHandle, i)
            ret, texName = TextureMapping.GetMapTextureName(materialHandle, i)
            tmElem = newdoc.createElement('TextureMapping')
            mtlElem.childNodes.append(tmElem)
            
            tmElem.setAttributeNode(newdoc.createAttribute('TextureFile'))
            tmElem.setAttribute('TextureFile', texFileName)
            
            tmElem.setAttributeNode(newdoc.createAttribute('TextureName'))
            tmElem.setAttribute('TextureName', texName)
            
        # alpha blend
        alphaBlendHandle = Material.GetAlphaBlendProperty(materialHandle)
        bEnable, srcBlend, destBlend = MPAlphaBlend.GetAlphaBlend(alphaBlendHandle)

        alphaBlendElem = newdoc.createElement('AlphaBlend')
        mtlElem.childNodes.append(alphaBlendElem)
        
        alphaBlendElem.setAttributeNode(newdoc.createAttribute('Enable'))
        alphaBlendElem.setAttribute('Enable', str(bEnable))
        
        alphaBlendElem.setAttributeNode(newdoc.createAttribute('SrcBlend'))
        alphaBlendElem.setAttribute('SrcBlend', str(srcBlend))
        
        alphaBlendElem.setAttributeNode(newdoc.createAttribute('DestBlend'))
        alphaBlendElem.setAttribute('DestBlend', str(destBlend))
        
        # alpha test
        alphaTestHandle = Material.GetAlphaTestProperty(materialHandle)
        bEnable, alphaTestType, ref = MPAlphaTest.GetAlphaTest(alphaTestHandle)

        alphaTestElem = newdoc.createElement('AlphaTest')
        mtlElem.childNodes.append(alphaTestElem)
        
        alphaTestElem.setAttributeNode(newdoc.createAttribute('Enable'))
        alphaTestElem.setAttribute('Enable', str(bEnable))
        
        alphaTestElem.setAttributeNode(newdoc.createAttribute('Type'))
        alphaTestElem.setAttribute('Type', str(alphaTestType))
        
        alphaTestElem.setAttributeNode(newdoc.createAttribute('Ref'))
        alphaTestElem.setAttribute('Ref', str(ref))
        
        # culling mode
        cullingModeHandle = Material.GetCullingModeProperty(materialHandle)
        cullingMode = MPCullingMode.GetCullingMode(cullingModeHandle)

        cullingModeElem = newdoc.createElement('CullingMode')
        mtlElem.childNodes.append(cullingModeElem)
        
        cullingModeElem.setAttributeNode(newdoc.createAttribute('Mode'))
        cullingModeElem.setAttribute('Mode', str(cullingMode))
        
        # depth state
        depthStateHandle = Material.GetDepthStateProperty(materialHandle)
        bEnable, bWriteEnable = MPDepthState.GetDepthState(depthStateHandle)
        
        depthStateElem = newdoc.createElement('DepthState')
        mtlElem.childNodes.append(depthStateElem)
        
        depthStateElem.setAttributeNode(newdoc.createAttribute('Enable'))
        depthStateElem.setAttribute('Enable', str(bEnable))
        
        depthStateElem.setAttributeNode(newdoc.createAttribute('WriteEnable'))
        depthStateElem.setAttribute('WriteEnable', str(bWriteEnable))
        
        # material ambient
        ambientHandle = Material.GetAmbientProperty(materialHandle)
        bEnable, r, g, b, a = MPAmbient.GetAmbient(ambientHandle)
        
        ambientElem = newdoc.createElement('Ambient')
        mtlElem.childNodes.append(ambientElem)
        
        ambientElem.setAttributeNode(newdoc.createAttribute('Enable'))
        ambientElem.setAttribute('Enable', str(bEnable))
        
        ambientElem.setAttributeNode(newdoc.createAttribute('a'))
        ambientElem.setAttribute('a', str(a))

        ambientElem.setAttributeNode(newdoc.createAttribute('r'))
        ambientElem.setAttribute('r', str(r))

        ambientElem.setAttributeNode(newdoc.createAttribute('g'))
        ambientElem.setAttribute('g', str(g))

        ambientElem.setAttributeNode(newdoc.createAttribute('b'))
        ambientElem.setAttribute('b', str(b))
        
        # material diffuse
        diffuseHandle = Material.GetDiffuseProperty(materialHandle)
        bEnable, r, g, b, a = MPDiffuse.GetDiffuse(diffuseHandle)

        diffuseElem = newdoc.createElement('Diffuse')
        mtlElem.childNodes.append(diffuseElem)
        
        diffuseElem.setAttributeNode(newdoc.createAttribute('Enable'))
        diffuseElem.setAttribute('Enable', str(bEnable))
        
        diffuseElem.setAttributeNode(newdoc.createAttribute('a'))
        diffuseElem.setAttribute('a', str(a))

        diffuseElem.setAttributeNode(newdoc.createAttribute('r'))
        diffuseElem.setAttribute('r', str(r))

        diffuseElem.setAttributeNode(newdoc.createAttribute('g'))
        diffuseElem.setAttribute('g', str(g))

        diffuseElem.setAttributeNode(newdoc.createAttribute('b'))
        diffuseElem.setAttribute('b', str(b))
        
        # material specular
        specularHandle = Material.GetSpecularProperty(materialHandle)
        bEnable, r, g, b, a, shiness = MPSpecular.GetSpecular(specularHandle)
        
        specularElem = newdoc.createElement('Specular')
        mtlElem.childNodes.append(specularElem)
        
        specularElem.setAttributeNode(newdoc.createAttribute('Enable'))
        specularElem.setAttribute('Enable', str(bEnable))
        
        specularElem.setAttributeNode(newdoc.createAttribute('a'))
        specularElem.setAttribute('a', str(a))

        specularElem.setAttributeNode(newdoc.createAttribute('r'))
        specularElem.setAttribute('r', str(r))

        specularElem.setAttributeNode(newdoc.createAttribute('g'))
        specularElem.setAttribute('g', str(g))

        specularElem.setAttributeNode(newdoc.createAttribute('b'))
        specularElem.setAttribute('b', str(b))        
        
        specularElem.setAttributeNode(newdoc.createAttribute('Shiness'))
        specularElem.setAttribute('Shiness', str(shiness))

        newdoc.writexml(open(fileName, 'w'),    newl = '\r\n', addindent = '    ', encoding = 'utf-8')
        newdoc.unlink()
        
    def __OnMenuSaveMaterialAs(self, e):
        pass
        
    def __OnMenuSaveModelAs(self, e):
        pass
        
    def __CreateUISubMeshList(self):
        """ Create sub mesh list UI """
        
        sbSubMesh = wx.StaticBox(self, -1, TD.SUB_MESH_LIST)
        sizerSubMesh = wx.StaticBoxSizer(sbSubMesh, wx.VERTICAL)
        self._listSubMesh = wx.ListCtrl(self, -1, style = wx.LC_REPORT | wx.LC_EDIT_LABELS)
        
        self._listSubMesh.InsertColumn(0, TD.MATERIAL_IDX)
        self._listSubMesh.InsertColumn(1, TD.MESH_FILE_NAME)
        
        sizerSubMesh.Add(self._listSubMesh, 1, wx.EXPAND|wx.ALL, border = 5)
        return sizerSubMesh
        
    def __CreateUIMaterialList(self):
        """ Create material list UI """
        
        sbMaterial = wx.StaticBox(self, -1, TD.MATERIAL_LIST)
        sizerMaterial = wx.StaticBoxSizer(sbMaterial, wx.VERTICAL)
        self._listMaterial = wx.ListCtrl(self, -1, style = wx.LC_REPORT)
        
        self._listMaterial.InsertColumn(0, TD.MATERIAL_FILE_NAME)
        self._listMaterial.InsertColumn(1, TD.MATERIAL_GROUP_SUB_MESH_COUNT)
        
        sizerMaterial.Add(self._listMaterial, 1, wx.EXPAND|wx.ALL, border = 5)
        return sizerMaterial
        
    def SetModelEntity(self, sceneNodeRef, entityIdx):
        self._sceneNodeRef = sceneNodeRef
        sceneNode = sceneNodeRef()
        if not sceneNode:
            return
            
        self._entityHandle = SceneNode.GetEntity(sceneNode.GetCoreHandle(), entityIdx)
        self.__UpdateUI(self._entityHandle)
        
    def __UpdateUI(self, entityHandle):
        if Entity.GetType(entityHandle) != Entity.ET_MODEL:
            return
            
        self.__UpdateUISubMeshList(entityHandle)
        self.__UpdateUIMaterialList(entityHandle)
            
    def __UpdateUISubMeshList(self, entityHandle):
        coreModelHandle = EntityModel.GetCoreModel(entityHandle)
        subMeshCount = Model.GetSubMeshCount(coreModelHandle)
        self._listSubMesh.DeleteAllItems()
        for i in xrange(0, subMeshCount):
            subMeshHandle = Model.GetSubMeshByIdx(coreModelHandle, i)
            fileName = Resource.GetResourceName(subMeshHandle)
            materialIdx = Mesh.GetMaterialIdx(subMeshHandle)
            index = self._listSubMesh.InsertStringItem(self._listSubMesh.GetItemCount(), unicode(materialIdx))
            self._listSubMesh.SetStringItem(index, 1, fileName.decode('gbk'))
        
    def __UpdateUIMaterialList(self, entityHandle):
        materialCount = EntityModel.GetMaterialCount(entityHandle)
        self._listMaterial.DeleteAllItems()
        for i in xrange(0, materialCount):
            ret, materialHandle, groupSubMeshCount = EntityModel.GetMaterialInfo(entityHandle, i)
            materialFileName = Material.GetFileName(materialHandle)
            index = self._listMaterial.InsertStringItem(self._listMaterial.GetItemCount(), materialFileName.decode('gbk'))
            self._listMaterial.SetStringItem(index, 1, unicode(groupSubMeshCount))
            
    def __OnSubMeshMtlIdxChanged(self, e):
        if not self.IsEditing():
            return

        if not e.GetLabel():
            return
                        
        newMtlIdx = e.GetLabel()
        
        coreModelHandle = EntityModel.GetCoreModel(self._entityHandle)
        subMeshHandle = Model.GetSubMeshByIdx(coreModelHandle, e.m_itemIndex)
        if not subMeshHandle:
            return

        Mesh.SetMaterialIdx(subMeshHandle, int(newMtlIdx))
        EntityModel.SortSubMeshByMaterialIdx(entityHandle)
        self.__UpdateUISubMeshList(entityHandle)
        e.Veto()
        
    def __OnActiveMaterial(self, e):
        if not self.IsEditing():
            return
            
        ret, materialHandle, groupSubMeshCount = EntityModel.GetMaterialInfo(self._entityHandle, e.m_itemIndex)
        if not ret:
            return
            
        dlg = wxDialogMaterialEdit(self)
        dlg.UpdateUI(materialHandle)
        dlg.ShowModal()
        dlg.Destroy()